<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Dojo_View_Helper_DataGrid
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id$
 */

/**
 * Zend_Calendar
 *
 * @category   Zend
 * @package    Zend_Dojo_View_Helper_DataGrid
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Dojo_View_Helper_DataGrid extends Zend_Dojo_View_Helper_Dijit
{
	const GRID_DATA = 'dojox.grid.DataGrid';
	const GRID_ENHANCED = 'dojox.grid.EnhancedGrid';
	const GRID_TREE = 'dojox.grid.TreeGrid';
	
	const THEME_NIHILO = 'nihilo';
	const THEME_TUNDRA = 'tundra';
	const THEME_SORIA = 'soria';
	
	/**
	 * Fields for grid
	 * @var array
	 */
	protected $_fields = array();
	
	/**
	 * Parameters for grid
	 * @var array
	 */
	protected $_params = array();
	
	/**
	 * Attributes for grid
	 * @var array
	 */
	protected $_attribs = array();
	
	/**
	 * Script captures
	 */
	protected $_scriptCaptures = array();
	
	/**
	 * Script capture info for the current capture
	 * @var array
	 */
	protected $_scriptCaptureInfo = array();
	
	/**
	 * Root node element type for layout elements
	 * @var string
	 */
	protected $_rootNode = 'table';
	
	/**
	 * Default grid type
	 * @var string
	 */
	protected static $_defaultGrid = self::GRID_DATA;
	
	/**
	 * Default theme
	 * @var string
	 */
	protected static $_defaultTheme = 'tundra';
	
	/**
	 * toString()
	 */
	public function __toString()
	{
		return $this->render();
	}
	
	/**
	 * Sets the default grid type to use
	 * 
	 * @param string $dojoType
	 */
	public static function setDefaultGrid($dojoType)
	{
		switch ($dojoType) {
			case self::GRID_ENHANCED:
			case self::GRID_TREE:
				self::$_defaultGrid = $dojoType;
				break;
			default:
				self::$_defaultGrid = self::GRID_DATA;
				break;
		}
	}
	
	/**
	 * Sets the default theme
	 * 
	 * @param string $theme
	 */
	public static function setDefaultTheme($theme)
	{
		switch ($theme) {
			case self::THEME_NIHILO:
			case self::THEME_SORIA:
				self::$_defaultTheme = $theme;
				break;
			default:
				self::$_defaultTheme = self::THEME_TUNDRA;
				break;
		}
	}
	
	/**
	 * Static getter for theme
	 * 
	 * @return string
	 */
	public static function getTheme()
	{
		return self::$_defaultTheme;
	}
	
	/**
	 * Static setter for theme
	 * 
	 * @param string $theme Name of the current them.
	 */
	public static function setTheme($theme)
	{
		self::$_defaultTheme = $theme;
	}
	
	/**
	 * DataGrid view helper
	 * 
	 * @param string $id
	 * @param array $attribs
	 */
	public function dataGrid($id, array $params = array(), array $attribs = array())
	{
		$this->_params = $params;
		$this->_attribs = $attribs;
		$this->_attribs['id'] = $id;
		
		// Layout
		if (array_key_exists('fields', $this->_params)) {
			foreach ($this->_params['fields'] as $field) {
				$this->addField($field['field'], $field['label'], $field['options']);
			}
			
			unset($this->_params['fields']);
		}
		
		return $this;
	}
	
	/**
	 * Render the grid
	 */
	public function render()
	{
		$this->_setGridType();
		
		// Add stylesheets to view
		$this->renderStylesheets();
		
		// Render grid based on style (declarative or programmatic)
		if ($this->_useProgrammatic()) {
			if (!$this->_useProgrammaticNoScript()) {
				$this->_renderJavascript();
			}
			
			return $this->_renderProgrammatic();
		}
		
		return $this->_renderDeclarative();
	}
	
	/**
	 * Renders stylesheets used by grid
	 */
	public function renderStylesheets()
	{
		Zend_Dojo_View_Helper_Dojo_Extended::addStylesheet('/dojox/grid/resources/Grid.css');
		
		$gridCss = '/dojox/grid/resources/' . self::getTheme() . '/';
		switch ($this->_dijit) {
			case self::GRID_DATA:
				$gridCss .= 'Grid.css';
				break;
			case self::GRID_ENHANCED:
				$gridCss .= 'enhancedGrid.css';
				break;
			case self::GRID_TREE:
				$gridCss .= 'treeGrid.css';
				break;
		}
		
		Zend_Dojo_View_Helper_Dojo_Extended::addStylesheet($gridCss);
	}
	
	/**
	 * Adds a field to the grid
	 * 
	 * @param string $field
	 * @param string $label
	 * @param array $options
	 */
	public function addField($field, $label, array $options = array())
	{
		if (array_key_exists($field, $this->_fields)) {
			throw new Zend_Exception(sprintf('Field "%s" already exists'), $field);
		}
		
		$this->_fields[$field] = array('field' => $field, 'label' => $label, 'options' => $options);
		return $this;
	}
	
	/**
	 * Adds script content to the grid
	 * 
	 * @param string $type 
	 * @param string $event
	 * @param string $content
	 * @param array $params
	 */
	public function addScript($type, $event, $content, array $params = array())
	{
		$this->_scriptCaptures[] = array(
			'type' => $type, 
			'event' => $event, 
			'content' => $content, 
			'params' => $params);
		
		return $this;
	}
	
	/**
	 * Begin capturing content for scripts
	 *
	 * @param  string $type
	 * @param  string $event
	 * @param  array $params
	 * @return void
	 */
	public function scriptCaptureStart($type, $event, array $params = array())
	{
		if (!empty($this->_scriptCaptureInfo)) {
			throw new Zend_Dojo_View_Exception('Already capturing script content');
		}
		
		$this->_scriptCaptureInfo = array('type' => $type, 'event' => $event, 'params' => $params);
		
		ob_start();
		return;
	}
	
	/**
	 * Finish capturing content for layout container
	 */
	public function scriptCaptureEnd()
	{
		if (empty($this->_scriptCaptureInfo)) {
			throw new Zend_Dojo_View_Exception('Not currently capturing script content');
		}
		
		$content = ob_get_clean();
		$this->addScript($this->_scriptCaptureInfo['type'], $this->_scriptCaptureInfo['event'], $content, 
			$this->_scriptCaptureInfo['params']);
		
		$this->_scriptCaptureInfo = array();
		return;
	}
	
	/**
	 * Render JavaScript for programmatic usage 
	 */
	protected function _renderJavascript()
	{
		$gridId = $this->_attribs['id'] . 'Grid';
		$layoutId = $this->_attribs['id'] . 'Layout';
		
		// Layout JavaScript
		$layoutJs = "var {$layoutId} = [\n";
		foreach ($this->_fields as $field) {
			$options = $field['options'];
			$options['field'] = $field['field'];
			$options['name'] = $field['label'];
			
			$layoutJs .= '            ';
			$layoutJs .= Zend_Json::encode($this->_jsonExpr($options), false, 
				array('enableJsonExprFinder' => true)) . ",\n";
		}
		$layoutJs .= "        ];";
		$this->_params['structure'] = new Zend_Json_Expr($layoutId);
		
		// Grid JavaScript
		$this->dojo->addJavascript("        var {$gridId};");
		
		$gridJs = "{$gridId} = new {$this->_dijit}(";
		$gridJs .= Zend_Json::encode($this->_jsonExpr($this->_params), false, 
			array('enableJsonExprFinder' => true)) . ",";
		$gridJs .= "document.createElement('div'))\n";
		$gridJs .= sprintf("        dojo.byId(\"%s\").appendChild({$gridId}.domNode);", 
			$this->_attribs['id']);
		
		// Script capture JavaScript
		$scriptJs = '';
		foreach ($this->_scriptCaptures as $script) {
			$content = trim($script['content'], "\r\n");
			$args = isset($script['params']['args']) ? $script['params']['args'] : '';
			
			$scriptJs .= "        dojo.connect({$gridId}, \"{$script['event']}\", function({$args}){{$content}});\n";
		}
		
		$js = "function(){\n        {$layoutJs}\n\n";
		$js .= "        {$gridJs}\n";
		$js .= "        {$gridId}.startup();\n\n";
		$js .= $scriptJs;
		$js .= "    }";
		$this->dojo->_addZendLoad($js);
	}
	
	/**
	 * Render for programmatic usage
	 */
	protected function _renderProgrammatic()
	{
		// Temporarily disable programmatic scripts to create grid manually
		$oldState = Zend_Dojo_View_Helper_Dojo_Extended::getRenderType();
		Zend_Dojo_View_Helper_Dojo::setUseProgrammatic(-1);
		
		// Prepare layout div
		$nodeType = 'div';
		$this->_attribs = $this->_prepareDijit($this->_attribs, $this->_params, 'layout');
		$html = '<' . $nodeType . $this->_htmlAttribs($this->_attribs) . "></$nodeType>\n";
		
		// Return to normal state
		Zend_Dojo_View_Helper_Dojo_Extended::setRenderType($oldState);
		
		return $html;
	}
	
	/**
	 * Sets the grid to use based on default grid type
	 * and parameters
	 */
	protected function _setGridType()
	{
		$this->_module = self::$_defaultGrid;
		
		if (array_key_exists('enhancedGrid', $this->_params)) {
			if (array_key_exists('plugins', $this->_params)) {
				$plugins = $this->_params['plugins'];
				if (array_key_exists('nestedSorting', $plugins)) {
					$this->dojo->requireModule('dojox.grid.enhanced.plugins.NestedSorting');
				}
				
				if (array_key_exists('indirectSelection', $plugins)) {
					$this->dojo->requireModule('dojox.grid.enhanced.plugins.IndirectSelection');
				}
				
				if (array_key_exists('dnd', $plugins)) {
					$this->dojo->requireModule('dojox.grid.enhanced.plugins.NestedSorting');
					$this->dojo->requireModule('dojox.grid.enhanced.plugins.DnD');
				}
			}
			$this->_module = self::GRID_ENHANCED;
			unset($this->_params['enhancedGrid']);
		} else if (array_key_exists('treeGrid', $this->_params)) {
			$this->_module = self::GRID_TREE;
			unset($this->_params['treeGrid']);
		} else if (array_key_exists('dataGrid', $this->_params)) {
			$this->_module = self::GRID_DATA;
			unset($this->_params['dataGrid']);
		}
		
		$this->_dijit = $this->_module;
	}
	
	/**
	 * Parses an array looking for keys that should be 
	 * Zend_Json_Expr()'s and coverts them.
	 */
	protected function _jsonExpr(array $data)
	{
		$jsonExprAttr = array('formatter', 'get', 'query', 'store');
		foreach ($jsonExprAttr as $exp) {
			if (array_key_exists($exp, $data)) {
				$data[$exp] = new Zend_Json_Expr($data[$exp]);
			}
		}
		
		return $data;
	}
}